Skip to content

feat: SSR redirects for index pages and folder navigation#69

Merged
rsbh merged 6 commits into
mainfrom
feat/ssr-redirects
May 14, 2026
Merged

feat: SSR redirects for index pages and folder navigation#69
rsbh merged 6 commits into
mainfrom
feat/ssr-redirects

Conversation

@rsbh
Copy link
Copy Markdown
Member

@rsbh rsbh commented May 14, 2026

Summary

  • SSR 307 redirects for index/folder pages — works without client JS
  • Shared tree-utils.ts with getFirstPageUrl, findFolderFirstPage, resolveDocsRedirect
  • Used by both entry-server.tsx (SSR) and DocsPage.tsx (client)
  • Added http-status-codes for readable status constants

Redirects

URL Behavior
/apis 307 → first API endpoint
/docs (no index) 307 → index_page or first page
/docs/guides (folder) 307 → first child page
/docs/nonexistent 404

Tests

16 tests covering all redirect scenarios.

Test plan

  • curl -I /apis → 307
  • curl -I /docs/guides → 307 to first child
  • /docs/nonexistent → 404
  • bun test src/lib/tree-utils.test.ts → 16 pass

🤖 Generated with Claude Code

- Move redirect logic to entry-server.tsx for SSR 307 redirects
- Shared tree-utils.ts used by both SSR and client-side DocsPage
- /apis → first API endpoint
- /docs (no index) → first page or index_page from config
- /docs/folder → first child page (derived from child URLs)
- Add http-status-codes for readable status constants
- 16 tests for tree-utils (getFirstPageUrl, findFolderFirstPage,
  resolveDocsRedirect)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chronicle Ready Ready Preview, Comment May 14, 2026 8:11am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Review Change Stack

Warning

Rate limit exceeded

@rsbh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 13 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a6eb01d5-f9a2-4e01-9196-a7def44449ae

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1d6e1 and b1286e3.

📒 Files selected for processing (2)
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/server/entry-server.tsx
📝 Walkthrough

Walkthrough

Adds tree traversal utilities and tests to compute docs redirect targets, uses them in DocsPage 404 handling, and performs early SSR redirects for unresolved docs and API index routes; also adds an http-status-codes dependency.

Changes

Docs and API Index Redirect Refactor

Layer / File(s) Summary
Tree utilities and test suite
packages/chronicle/src/lib/tree-utils.ts, packages/chronicle/src/lib/tree-utils.test.ts
NodeType, getFirstPageUrl, findFolderFirstPage, and resolveDocsRedirect added; tests validate first-page selection, nested folder resolution, index_page precedence, and null cases.
Client docs page integration
packages/chronicle/src/pages/DocsPage.tsx
DocsPage 404 handling now calls resolveDocsRedirect(slug, tree, contentConfig) and navigates to the returned URL when present, removing previous local redirect logic.
Server-side early-exit redirects
packages/chronicle/src/server/entry-server.tsx
SSR fetch handler short-circuits: ApiIndex redirects to first API URL via getFirstApiUrl, DocsPage unresolved routes redirect to resolveDocsRedirect result (prefixed by version when needed) using temporary redirect status.
HTTP status codes runtime dependency
packages/chronicle/package.json
Added http-status-codes ^2.3.0 for status code constants used in server redirects.

Sequence Diagram

sequenceDiagram
  participant Client
  participant EntryServer as entry-server.fetch
  participant getFirstApiUrl as getFirstApiUrl(apiSpecs)
  participant resolveDocsRedirect as resolveDocsRedirect(slug, tree, contentConfig)
  Client->>EntryServer: request (route)
  EntryServer->>EntryServer: detect RouteType
  EntryServer->>getFirstApiUrl: if ApiIndex -> getFirstApiUrl(apiSpecs)
  getFirstApiUrl-->>EntryServer: apiUrl?
  EntryServer->>Client: HTTP 307 Location: apiUrl (if present)
  EntryServer->>resolveDocsRedirect: if DocsPage and no page -> compute redirect
  resolveDocsRedirect-->>EntryServer: redirectUrl?
  EntryServer->>Client: HTTP 307 Location: versionPrefix+redirectUrl (if present)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • rohilsurana
  • rohanchkrabrty
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: implementing SSR redirects for index pages and folder navigation, which is the core objective across all modified files.
Description check ✅ Passed The PR description is directly related to the changeset, providing a clear summary, redirect behavior table, test coverage details, and a concrete test plan that aligns with the implemented changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ssr-redirects

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/chronicle/src/lib/tree-utils.test.ts (2)

85-113: ⚡ Quick win

Add a regression test for index_page on non-root slugs.

Please add a case asserting that index_page does not override folder/non-root redirects (or 404 paths).

💡 Proposed test case
 describe('resolveDocsRedirect', () => {
@@
   test('index_page takes priority over first page', () => {
     expect(resolveDocsRedirect(['docs'], tree, { dir: 'docs', index_page: 'custom' }))
       .toBe('/docs/custom')
   })
+
+  test('does not apply index_page for nested slugs', () => {
+    expect(resolveDocsRedirect(['docs', 'guides'], tree, { dir: 'docs', index_page: 'custom' }))
+      .toBe('/docs/guides/install')
+  })
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/lib/tree-utils.test.ts` around lines 85 - 113, Add a
regression test in tree-utils.test.ts for resolveDocsRedirect to ensure
index_page only applies to the content root: call resolveDocsRedirect with a
non-root slug (e.g., ['docs', 'guides']) and content config that contains dir:
'docs' plus index_page: 'custom' and assert it still redirects to the folder's
first child (e.g., '/docs/guides/install') rather than '/docs/custom'; use the
existing test pattern and the same tree fixture so the test name and
expectations match the other cases.

3-3: ⚡ Quick win

Use project path alias instead of relative import.

Switch this test import to @/lib/tree-utils for consistency with the repo alias policy.

💡 Proposed fix
-import { getFirstPageUrl, findFolderFirstPage, resolveDocsRedirect } from './tree-utils'
+import { getFirstPageUrl, findFolderFirstPage, resolveDocsRedirect } from '@/lib/tree-utils'

As per coding guidelines **/*.{ts,tsx}: Use path alias @/*./src/* configured in tsconfig and vite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/lib/tree-utils.test.ts` at line 3, Update the test
import to use the project path alias: replace the relative import of the module
used by tests (import { getFirstPageUrl, findFolderFirstPage,
resolveDocsRedirect } from './tree-utils') with the aliased path import from
'@/lib/tree-utils' so the tests follow the repo's tsconfig/vite alias policy and
resolve the same exported symbols (getFirstPageUrl, findFolderFirstPage,
resolveDocsRedirect).
packages/chronicle/src/server/entry-server.tsx (1)

47-57: ⚡ Quick win

Move API index redirect before page-tree fetch.

For RouteType.ApiIndex, lines [47-50] still load docs tree/page before returning the 307. Reordering this keeps the redirect path truly early-exit and reduces SSR work.

💡 Suggested structure
-    const [tree, page] = await Promise.all([
-      getPageTree(),
-      route.type === RouteType.DocsPage ? getPage(route.slug) : Promise.resolve(null),
-    ]);
-    // SSR redirects for index pages
     if (route.type === RouteType.ApiIndex) {
       const firstUrl = getFirstApiUrl(apiSpecs);
       if (firstUrl) {
         return new Response(null, { status: StatusCodes.TEMPORARY_REDIRECT, headers: { Location: firstUrl } });
       }
     }
+
+    const [tree, page] = await Promise.all([
+      getPageTree(),
+      route.type === RouteType.DocsPage ? getPage(route.slug) : Promise.resolve(null),
+    ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/chronicle/src/server/entry-server.tsx` around lines 47 - 57, The API
index redirect runs after expensive page-tree/page fetches; move the
RouteType.ApiIndex early-exit before calling getPageTree() and getPage() so SSR
work is avoided when redirecting. Specifically, check if route.type ===
RouteType.ApiIndex, call getFirstApiUrl(apiSpecs) and return the
TEMPORARY_REDIRECT response immediately if a URL exists, and only then perform
the Promise.all([getPageTree(), ...]) calls for non-ApiIndex routes (functions:
getPageTree, getPage, getFirstApiUrl; symbol: RouteType.ApiIndex).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/chronicle/src/lib/tree-utils.ts`:
- Around line 43-45: The current code always redirects missing doc slugs to
contentConfig?.index_page; change it so the redirect to
`/${contentConfig.dir}/${contentConfig.index_page}` only happens when the
requested slug is the content root (e.g., empty or "/") rather than any
nonexistent slug. Update the conditional around contentConfig?.index_page to
also check the incoming slug parameter (or request path) is empty/root (not a
non-empty missing slug) so that non-root missing slugs fall through to 404; keep
references to contentConfig and index_page when locating the change in
tree-utils.ts.

---

Nitpick comments:
In `@packages/chronicle/src/lib/tree-utils.test.ts`:
- Around line 85-113: Add a regression test in tree-utils.test.ts for
resolveDocsRedirect to ensure index_page only applies to the content root: call
resolveDocsRedirect with a non-root slug (e.g., ['docs', 'guides']) and content
config that contains dir: 'docs' plus index_page: 'custom' and assert it still
redirects to the folder's first child (e.g., '/docs/guides/install') rather than
'/docs/custom'; use the existing test pattern and the same tree fixture so the
test name and expectations match the other cases.
- Line 3: Update the test import to use the project path alias: replace the
relative import of the module used by tests (import { getFirstPageUrl,
findFolderFirstPage, resolveDocsRedirect } from './tree-utils') with the aliased
path import from '@/lib/tree-utils' so the tests follow the repo's tsconfig/vite
alias policy and resolve the same exported symbols (getFirstPageUrl,
findFolderFirstPage, resolveDocsRedirect).

In `@packages/chronicle/src/server/entry-server.tsx`:
- Around line 47-57: The API index redirect runs after expensive page-tree/page
fetches; move the RouteType.ApiIndex early-exit before calling getPageTree() and
getPage() so SSR work is avoided when redirecting. Specifically, check if
route.type === RouteType.ApiIndex, call getFirstApiUrl(apiSpecs) and return the
TEMPORARY_REDIRECT response immediately if a URL exists, and only then perform
the Promise.all([getPageTree(), ...]) calls for non-ApiIndex routes (functions:
getPageTree, getPage, getFirstApiUrl; symbol: RouteType.ApiIndex).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a4e191d1-de0f-4901-9202-59730ee4a07d

📥 Commits

Reviewing files that changed from the base of the PR and between e23cfae and 3dd0b3f.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/chronicle/package.json
  • packages/chronicle/src/lib/tree-utils.test.ts
  • packages/chronicle/src/lib/tree-utils.ts
  • packages/chronicle/src/pages/DocsPage.tsx
  • packages/chronicle/src/server/entry-server.tsx

Comment thread packages/chronicle/src/lib/tree-utils.ts Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread packages/chronicle/src/pages/DocsPage.tsx Outdated
Comment thread packages/chronicle/src/server/entry-server.tsx
Strip version prefix from slug before resolving redirect, use
version-specific content config, prepend version prefix to redirect URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@rsbh rsbh merged commit 44d6e27 into main May 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants